#pragma once

#include "serde.h"

namespace sfc::serde {

class Node;

struct Null {
  void fmt(auto& f) const {
    const auto s = Str{"null"};
    s.fmt(f);
  }
};

using List = Vec<Node>;
using Dict = VecMap<String, Node>;

class [[nodiscard]] Node {
  using Inn = Variant<Null, bool, i64, f64, String, List, Dict>;
  Inn _inn;

 public:
  Node();
  ~Node();
  Node(Node&&) noexcept;
  Node& operator=(Node&&) noexcept;

  explicit Node(bool val);
  explicit Node(i32 val);
  explicit Node(u32 val);
  explicit Node(i64 val);
  explicit Node(u64 val);
  explicit Node(f64 val);

  explicit Node(Str val);
  explicit Node(cstr_t val);

  explicit Node(List val);
  explicit Node(Dict val);

 public:
  auto as_null() const -> Option<Null>;
  auto as_bool() const -> Option<bool>;
  auto as_int() const -> Option<i64>;
  auto as_flt() const -> Option<f64>;
  auto as_str() const -> Option<Str>;
  auto as_list() const -> Option<const List&>;
  auto as_dict() const -> Option<const Dict&>;

  auto len() const -> usize;

  auto operator[](usize idx) const -> const Node&;
  void push(Node val);

  auto operator[](Str key) const -> const Node&;
  void insert(Str key, Node val);

  void fmt(auto& f) const {
    _inn.map([&](const auto& val) { f.write(val); });
  }
};

struct Serializer {
  using Node = serde::Node;
  using List = serde::List;
  using Dict = serde::Dict;

  auto ser_null() const -> Node {
    return Node{};
  }

  auto ser_bool(bool val) const -> Node {
    return Node{val};
  }

  auto ser_int(i64 val) const -> Node {
    return Node{val};
  }

  auto ser_flt(f64 val) const -> Node {
    return Node{val};
  }

  auto ser_str(Str val) const -> Node {
    return Node{val};
  }

  auto ser_list(auto iter) const -> Node {
    auto res = Node{List{}};
    iter->for_each([&](auto& item) { res.push(this->ser(item)); });
    return res;
  }

  auto ser_dict(auto iter) const -> Node {
    auto res = Node{Dict{}};
    iter->for_each([&](auto& item) {
      const auto& [k, v] = item;
      res.insert(k, this->ser(v));
    });
    return res;
  }

  auto ser_struct(auto iter) const -> Node {
    auto res = Node{Dict{}};
    iter.map([&](auto& item) {
      const auto& [k, v] = item;
      res.insert(k, this->ser(v));
    });
    return res;
  }

  auto ser(const auto& val) const -> Node {
    if constexpr (requires() { val.serialize(*this); }) {
      return val.serialize(*this);
    } else if constexpr (requires() { Serde{val}; }) {
      return Serde{val}.serialize(*this);
    } else {
      return Node{};
    }
  }
};

class Deserializer {
  const Node& _node;

 public:
  Deserializer(const Node& node) : _node{node} {}

  auto des_null() const -> Option<Null> {
    return _node.as_null();
  }

  auto des_bool() const -> Option<bool> {
    return _node.as_bool();
  }

  auto des_int() const -> Option<i64> {
    return _node.as_int();
  }

  auto des_flt() const -> Option<f64> {
    return _node.as_flt() || _node.as_int().map([](auto x) { return static_cast<f64>(x); });
  }

  auto des_str() const -> Option<Str> {
    return _node.as_str();
  }

  auto des_list() const -> Option<const List&> {
    return _node.as_list();
  }

  auto des_dict() const -> Option<const Dict&> {
    return _node.as_dict();
  }

  template <class T>
  auto des(T& val) const -> bool {
    if constexpr (requires { T::deserialize(*this); }) {
      return T::deserialize(*this).map([&](auto& x) { return val = mem::move(x); });
    } else {
      return Serde<T>::deserialize(*this, val);
    }
  }
};

template <class T>
auto serialize(const T& val) -> Node {
  auto se = Serializer{};
  return se.ser(val);
}

template <class T>
auto deserialize(const Node& node) -> Option<T> {
  auto de = Deserializer{node};
  return de.des<T>();
}

}  // namespace sfc::serde
